// Copyright 2011 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


/**
 * @fileoverview Define the controller for bite.layer.AddUrl
 * console.  The object defines the functionality specifically for this
 * controller and does not affect the outside world.  However, it does define
 * a set of signals that can be used by external sources to perform actions
 * when the controller performs certain tasks.
 *
 * The AddUrl console is inteneded to have only a single instance.
 *
 * @author jasonstredwick@google.com (Jason Stredwick)
 */


goog.provide('bite.layer.AddUrl');
goog.provide('bite.layer.AddUrlEventType');

goog.require('bite.common.events.DataBearingEvent');
goog.require('bite.common.mvc.helper');
goog.require('bite.layer.ConsoleBehavior');
goog.require('bite.layer.soy.AddUrl');
goog.require('goog.events.EventTarget');
goog.require('goog.events.EventType');



/**
 * Constructs an instance of a AddUrl controller.  The constructor
 * creates the object in a default state that has not been mapped to a view.
 * Note: As I need to use goog.mixin to fake multiple inheritance, I believe I
 *       can only extend the console once, so I am using EventTarget for that.
 * @extends {goog.events.EventTarget}
 * @constructor
 * @export
 */
bite.layer.AddUrl = function() {
  goog.base(this);

  /**
   * Console element managed by this controller.  Defaults to null meaning the
   * controller is not attached to a model.
   * @type {Element}
   * @private
   */
  this.console_ = null;

  /**
   * Whether or not the console's model is initialized.
   * @type {boolean}
   * @private
   */
  this.isModelInitialized_ = false;

  /**
   * Whether or not the console's view is initialized.
   * @type {boolean}
   * @private
   */
  this.isViewInitialized_ = false;

  /**
   * The input box for the url entered by the user.
   * @type {Element}
   * @private
   */
  this.url_ = null;
};
goog.inherits(bite.layer.AddUrl, goog.events.EventTarget);
goog.mixin(bite.layer.AddUrl.prototype,
           bite.layer.ConsoleBehavior.prototype);
goog.addSingletonGetter(bite.layer.AddUrl);


/**
 * The console's id.
 * @type {string}
 * @private
 */
bite.layer.AddUrl.CONSOLE_ID_ = 'layer-add-url';


/**
 * Element ids for the console's buttons.
 * @enum {string}
 * @private
 */
bite.layer.AddUrl.Button_ = {
  ADD: 'layer-add-url-add',
  CANCEL: 'layer-add-url-cancel'
};


/**
 * Defines key values used to pass data in and out of the console.
 * @enum {string}
 * @export
 */
bite.layer.AddUrl.Data = {
  URL: 'url'
};


/**
 * Element ids for the console's inputs.
 * @enum {string}
 * @private
 */
bite.layer.AddUrl.Input_ = {
  // Layer Manifest Url Text Box
  URL: 'layer-add-url-input-url'
};


/**
 * Event types supported by this console.
 * @enum {string}
 * @export
 */
bite.layer.AddUrlEventType = {
  ADD: 'add',
  CANCEL: 'cancel'
};


/**
 * Handler that is called when the add button is pressed.
 * @private
 */
bite.layer.AddUrl.prototype.handleOnAdd_ = function() {
  var data = {};
  data[bite.layer.AddUrl.Data.URL] = this.url_.value;

  var type = bite.layer.AddUrlEventType;
  var event = new bite.common.events.DataBearingEvent(type.ADD, data);

  this.dispatchEvent(event);
};


/**
 * Handler that is called when the cancel button is pressed.
 * @private
 */
bite.layer.AddUrl.prototype.handleOnCancel_ = function() {
  var type = bite.layer.AddUrlEventType;
  var event = new bite.common.events.DataBearingEvent(type.CANCEL);
  this.dispatchEvent(event);
};


/**
 * Initialize the AddUrl Console by creating the model and hooking
 * functions to the model.  If a baseUrl is given and the view is not yet
 * initialized then this function will try to initialize it.  Nothing happens
 * if the console is already initialized.
 * @param {string=} opt_baseUrl The baseUrl required for relative references.
 * @return {boolean} Whether or not the model is initialized.  It will also
 *     return true if the model is already initialized.
 * @export
 */
bite.layer.AddUrl.prototype.init = function(opt_baseUrl) {
  // Checking isDef allows for baseUrl to be the empty string.
  if (goog.isDef(opt_baseUrl) && !this.isViewInitialized_) {
    this.initView(opt_baseUrl);
  }

  if (this.isModelInitialized_) {
    return true;
  }

  var helper = bite.common.mvc.helper;
  this.console_ = helper.initModel(bite.layer.soy.AddUrl.getModel);

  if (this.console_ &&
      this.setupBehavior_(this.console_) &&
      this.setupButtonHandlers_(this.console_) &&
      this.setupTextInputs_(this.console_)) {
    this.isModelInitialized_ = true;
  } else {
    this.undoSetup_();
  }

  return this.isModelInitialized_;
};


/**
 * Given an optional baseUrl, initialize the view for the console by adding
 * its css to the page.
 * @param {string=} opt_baseUrl The baseUrl required for relative references.
 * @return {boolean} Whether or not the view was added.  It also returns true
 *     if the view is already initialized.
 * @export
 */
bite.layer.AddUrl.prototype.initView = function(opt_baseUrl) {
  var helper = bite.common.mvc.helper;
  if (!this.isViewInitialized_ &&
      helper.initView(bite.layer.soy.AddUrl.getView, opt_baseUrl)) {
    this.isViewInitialized_ = true;
  }

  return this.isViewInitialized_;
};


/**
 * Determines if the console is constructed or not.
 * @return {boolean} Whether or not the console is initialized.
 * @export
 */
bite.layer.AddUrl.prototype.isInitialized = function() {
  return this.isViewInitialized_ && this.isModelInitialized_;
};


/**
 * Takes an object that contains data for the various inputs and sets the
 * input elements to the appropriate values.
 * @param {{url: string}} data The data used to fill out the text input.
 * @return {boolean} Whether or not the console was updated.
 * @export
 */
bite.layer.AddUrl.prototype.reset = function(data) {
  if (!this.isInitialized()) {
    return false;
  }

  // Ensure that data is an object in case if was null, undefined, or something
  // else.
  data = data || {};

  return this.update(data);
};


/**
 * Setup an element to be controlled by the behaviors.
 * @param {!Element} srcElement The element to control.
 * @return {boolean} Setup was successful.
 * @private
 */
bite.layer.AddUrl.prototype.setupBehavior_ = function(srcElement) {
  // The "this" object has to be cast to a generic object before it's
  // "inherited" functions can be called.  This is an issue caused by the
  // the jscompiler to be able to recognize functions added to the object
  // uisng goog.mixin, which has to be used because the jscompiler does not
  // also support multiple inheritence.
  var obj = /** @type {Object} */ (this);
  obj.setBehavior(srcElement);
  obj.hide();
  obj.disable();

  return true;
};


/**
 * Setup buttons in console.
 * @param {!Element} srcElement The element to search from.
 * @return {boolean} Successfully found and attached handlers.
 * @private
 */
bite.layer.AddUrl.prototype.setupButtonHandlers_ = function(srcElement) {
  var helper = bite.common.mvc.helper;

  var add = helper.getElement(bite.layer.AddUrl.Button_.ADD, srcElement);
  goog.events.listen(add, goog.events.EventType.CLICK,
                     goog.bind(this.handleOnAdd_, this));

  var cancel = helper.getElement(bite.layer.AddUrl.Button_.CANCEL, srcElement);
  goog.events.listen(cancel, goog.events.EventType.CLICK,
                     goog.bind(this.handleOnCancel_, this));

  return true;
};


/**
 * Setup input elements from the newly created console and store them.
 * @param {!Element} srcElement The element to search from.
 * @return {boolean} Successfully found elements or not.
 * @private
 */
bite.layer.AddUrl.prototype.setupTextInputs_ = function(srcElement) {
  var helper = bite.common.mvc.helper;
  this.url_ = helper.getElement(bite.layer.AddUrl.Input_.URL, srcElement);

  return true;
};


/**
 * Reset the model portion of the controller back to an uninitialized state.
 * @private
 */
bite.layer.AddUrl.prototype.undoSetup_ = function() {
  // Make sure and remove the DOM Element from the document upon failure.
  if (this.console_) {
    var helper = bite.common.mvc.helper;
    helper.removeElementById(bite.layer.AddUrl.CONSOLE_ID_);
  }

  this.console_ = null;
  this.url_ = null;
};


/**
 * Takes an object that contains data for the various inputs and sets the
 * input elements to the appropriate values.
 * @param {{url: string}} data The data used to fill out the text input.
 * @return {boolean} Whether or not the console was updated.
 * @export
 */
bite.layer.AddUrl.prototype.update = function(data) {
  if (!this.isInitialized()) {
    return false;
  }

  if (bite.layer.AddUrl.Data.URL in data) {
    this.url_ = data[bite.layer.AddUrl.Data.URL];
  }

  return true;
};

